package com.erp.common.advice;


import com.erp.common.enums.ResponseCode;
import com.erp.common.exception.BaseBizException;
import com.erp.common.exception.BaseException;
import com.erp.common.exception.BaseParamsException;
import com.erp.common.exception.BaseSystemsException;
import com.erp.common.response.BaseResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.List;
import java.util.Set;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionAdvice {

    /**
     * NoHandlerFoundException 404 异常处理
     */
    @ExceptionHandler(value = NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public BaseResponse handlerNoHandlerFoundException(NoHandlerFoundException e) {
        BaseResponse<Object> baseResponse = BaseResponse.fail(ResponseCode.NOT_FOUND.getCode(),e,e.getMessage());
        return baseResponse;
    }

    /**
     * HttpRequestMethodNotSupportedException 405 异常处理
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public BaseResponse handlerHttpRequestMethodNotSupportedException(
            HttpRequestMethodNotSupportedException e) {
        BaseResponse<Object> commonResponse = BaseResponse.fail(ResponseCode.METHOD_NOT_ALLOWED.getCode(),e, e.getMessage());
        return commonResponse;
    }

    /**
     * HttpMediaTypeNotSupportedException 415 异常处理
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    public BaseResponse handlerHttpMediaTypeNotSupportedException(
            HttpMediaTypeNotSupportedException e) {
        BaseResponse<Object> commonResponse = BaseResponse.fail(ResponseCode.UNSUPPORTED_MEDIA_TYPE.getCode(),e, e.getMessage());
        return commonResponse;
    }

    /**
     *  全局业务异常处理
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
//    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public BaseResponse antHillException(Exception e) {

        BaseResponse  commonResponse=exceptionHandler(e);

        return commonResponse;
    }

    private BaseResponse exceptionHandler(Exception e) {

        outPutError(Exception.class, ResponseCode.SYSTEM_ERROR, e);
        BaseResponse<Object> commonResponse = null;
        if (e instanceof BaseException) {
            BaseException e1 = (BaseException) e;
            if (e1 instanceof BaseParamsException) {
                log.info("BaseParamsException: responseCode={},message={},e={}", e1.getCode(), e1.getMessage(), e1.toString());
            } else if (e1 instanceof BaseBizException) {
                log.info("BaseBizException: responseCode={},message={},e={}", e1.getCode(), e1.getMessage(), e1.toString());
            } else if (e1 instanceof BaseSystemsException) {
                log.info("BaseSystemsException: responseCode={},message={},e={}", e1.getCode(), e1.getMessage(), e1.toString());
            }

            if (null != e1.getCode()) {
                commonResponse = BaseResponse.fail(e1.getCode(), e1.getMessage());
            } else {
                commonResponse = BaseResponse.fail(ResponseCode.BUSINESS_ERROR.getCode(), e1.getMessage());
            }
        } else if (e instanceof UndeclaredThrowableException) {
            // JDK 动态代理 异常 UndeclaredThrowableException 没有具体message,处理获取具体异常的message
            commonResponse = BaseResponse.fail(ResponseCode.SYSTEM_ERROR.getCode(),
                    ((UndeclaredThrowableException) e).getUndeclaredThrowable().getMessage());
        } else {

            commonResponse = BaseResponse.fail(ResponseCode.SYSTEM_ERROR.getCode(), e.getMessage());
        }

        return commonResponse;

    }


    /**
     * HttpMessageNotReadableException 参数错误异常
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public BaseResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        outPutError(HttpMessageNotReadableException.class, ResponseCode.PARAM_ERROR, e);
        BaseResponse<Object> commonResponse = BaseResponse.fail(ResponseCode.PARAM_ERROR.getCode(), e.getMessage());
        return commonResponse;
    }

    /**
     * ConstraintViolationException 参数错误异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public BaseResponse handleConstraintViolationException(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        if (log.isDebugEnabled()) {
            for (ConstraintViolation error : constraintViolations) {
                log.error("{} -> {}", error.getPropertyPath(), error.getMessageTemplate());
            }
        }


        if (constraintViolations.isEmpty()) {
            log.error("validExceptionHandler error fieldErrors is empty");
            return BaseResponse.fail(ResponseCode.BUSINESS_ERROR.getCode(), e.getMessage());
        }
        return BaseResponse.fail(ResponseCode.PARAM_ERROR.getCode(), e, e.getMessage());
    }

    /**
     * MethodArgumentNotValidException 参数错误异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public BaseResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();
        return getBindResultDTO(bindingResult);
    }

    /**
     * BindException 参数错误异常
     */
    @ExceptionHandler(BindException.class)
    public BaseResponse handleBindException(BindException e) {
        outPutError(BindException.class, ResponseCode.PARAM_ERROR, e);
        BindingResult bindingResult = e.getBindingResult();
        return getBindResultDTO(bindingResult);
    }

    private BaseResponse getBindResultDTO(BindingResult bindingResult) {
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        if (log.isDebugEnabled()) {
            for (FieldError error : fieldErrors) {
                log.error("{} -> {}", error.getDefaultMessage(), error.getDefaultMessage());
            }
        }

        if (fieldErrors.isEmpty()) {
            log.error("validExceptionHandler error fieldErrors is empty");
            return BaseResponse.fail(ResponseCode.BUSINESS_ERROR.getCode(),"validExceptionHandler error fieldErrors is empty");
        }

        return BaseResponse.fail(ResponseCode.PARAM_ERROR.getCode(),fieldErrors.get(0).getDefaultMessage());

    }


    public void outPutError(Class errorType, Enum secondaryErrorType, Throwable throwable) {
        log.error("[{}] {}: {}", errorType.getSimpleName(), secondaryErrorType, throwable.getMessage(),
                throwable);
    }

    public void outPutErrorWarn(Class errorType, Enum secondaryErrorType, Throwable throwable) {
        log.warn("[{}] {}: {}", errorType.getSimpleName(), secondaryErrorType, throwable.getMessage());
    }

}